#include "server.h"
#include "dht11.h"
#include "cJSON.h"
#include "leds.h"
#include "lcd1602.h"
#include "ctype.h"
#include "fan.h"
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>


#define LISTENQ 10

/*
 *  bind server ip address and port;
 *  then start listening the connection.
 */
int start_server()
{
    int     listenfd;
    struct sockaddr_in  servaddr;

    listenfd = socket(AF_INET, SOCK_STREAM, 0);
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(SERVER_PORT);

    if (bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) <0){
        perror("[error] server bind error");
        exit(-1);
    }

    if (listen(listenfd, LISTENQ) < 0){
        perror("[error] server listen error");
        exit(-1);
    }
    printf("[log] server is listening ...\n");
    return listenfd;
}

/*
 *  waiting for next connetion,
 *  if error, return -1;
 *  else return the connected fd.
 */
int wait_for_connection(int listenfd)
{
    int connfd;
    if ( (connfd = accept(listenfd, (struct sockaddr*)NULL, NULL)) < 0){
        perror("[error] accept error");
        return -1;
    }
    return connfd;
}


void do_temphum_request(int connfd)
{
    time_t  ticks;
    char buffer[4096];
    struct dht11_result result;
    int ret;

    memset(buffer, 0, sizeof(buffer));
    /* get system time */
    ticks = time(NULL);
    /* get tempture and humdity by dht11 sensor */
    ret = read_dht11(&result);

    /*  then send json string the client */
    if (ret != 0){  /* ok. */
        snprintf(buffer, sizeof(buffer),
               "{\"target\":\"humTemp\","
                "\"result\": { \"time\"     : \"%.24s\", "
                "  \"tempture\" :   %d  , "
                "  \"humidity\" :   %d }}\r\n",
                ctime(&ticks), result.tempture, result.humidity);
    }else{  /* error */
        snprintf(buffer, sizeof(buffer),
                "{\"target\":\"humTemp\","
                "\"result\": { \"time\"     : \"%.24s\", "
                "  \"tempture\" :   %d  , "
                "  \"humidity\" :   %d }}\r\n",
                ctime(&ticks), -1, -1);
    }
    write(connfd, buffer, strlen(buffer));
    fprintf(stderr,"[log] write to client finished\n");
    write(STDOUT_FILENO, buffer, strlen(buffer));  /* print result for debug. */
}

// JSON format.
// {
//    "leds": ["off", "on", "on", "off"]
// }
void do_leds_request(int connfd)
{
    char            buffer[4096];
    size_t          nread, ntotal;
    cJSON           *root, *leds, *led_item;
    int             i;

    ntotal = 0;
    memset(buffer, 0, sizeof(buffer));
    while ( (nread = read(connfd, buffer+ntotal, 4096)) != 0){
        ntotal += nread;
        if (nread < 0){
            fprintf(stderr, "[error] server read error.\n");
            return;
        }
    }
    fprintf(stdout, "[log] received message: %s.\n", buffer);
    root = cJSON_Parse(buffer);
    if (root == NULL){
        fprintf(stderr, "[error] cJOSN_Parse error.\n");
        return;
    }

    leds = cJSON_GetObjectItem(root, "leds");
    if (leds == NULL){
        fprintf(stderr, "[error] cJSON_GetObjectItem error.\n");
        return;
    }
    if (cJSON_GetArraySize(leds) != LED_COUNT){
        fprintf(stderr, "[error] led_count = %d is error.\n", cJSON_GetArraySize(leds));
        return;
    }

    /*
     *  do leds operations.
     */
    led_init();
    for (i = 0; i< LED_COUNT; ++i){
        led_item = cJSON_GetArrayItem(leds, i);
        if (led_item == NULL){
            fprintf(stderr, "[error] cJSON_GetArrayItem %d error.\n", i);
            continue;
        }
        if (!strcasecmp(led_item->valuestring, "on")){
            fprintf(stdout, "[log] ready to light led[%d].\n", i);
            light_led(i);
        }else if (!strcasecmp(led_item->valuestring, "off")){
            fprintf(stdout, "[log] ready to close led[%d].\n", i);
            close_led(i);
        }else{
            fprintf(stderr, "[error] cJSON invilad string %s error.\n", led_item->valuestring);
            continue;
        }
    }

    cJSON_Delete(root);
}

/*
 *  {
 *    "cmd" : "getTempHum", // "setLeds" //
 *     ....
 *  }
 */
int deal_request(int connfd)
{
    char        buffer[4096];
    size_t      nread;
    cJSON       *root, *cmd;

    memset(buffer, 0, sizeof(buffer));
    nread = read(connfd, buffer, 4096);
    if (nread <= 0){
        fprintf(stderr, "[error] read client error.");
        return -1;
    }

    fprintf(stdout, "[log] received message: %s.\n", buffer);
    root = cJSON_Parse(buffer);
    if (root == NULL){
        fprintf(stderr, "[error] cJOSN_Parse error.\n");
        return -1;
    }

   cmd = cJSON_GetObjectItem(root, "cmd");
   if (cmd == NULL){
       fprintf(stderr, "[error] cJSON_GetObjectItem 'cmd' error.\n");
       cJSON_Delete(root);
       return -1;
   }

    if (!strcasecmp(cmd->valuestring, "getTempHum")){
        // get tempaerture and humdity request.
        do_temphum_request(connfd);

    }else if (!strcasecmp(cmd->valuestring, "setLeds")){
        // set leds' statues.
        control_leds(root);
    }else if (!strcasecmp(cmd->valuestring, "setFan")){
        // set fan's statue
        control_fan(root);
    }else{
        fprintf(stderr, "[error] unkonw command: %s.\n", cmd->valuestring);
    }

    cJSON_Delete(root);
}

/*
 *  { "cmd" : "setLeds", "leds" : ["on, "off", "on", "off"]}
 */
void control_leds(struct cJSON* root)
{
    cJSON   *leds, *led_item;
    int     i;
    int led_result[LED_COUNT] = {0};
    char buffer[4096];

    if (root == NULL){
        fprintf(stderr, "[error] contorl_les error.\n");
        return;
    }

    leds = cJSON_GetObjectItem(root, "leds");
    if (leds == NULL){
        fprintf(stderr, "[error] cJSON_GetObjectItem 'leds' error.\n");
        return;
    }
    if (cJSON_GetArraySize(leds) != LED_COUNT){
        fprintf(stderr, "[error] led_count = %d is error.\n", cJSON_GetArraySize(leds));
        return;
    }
    /*
     *  do leds operations.
     */
    led_init();
    for (i = 0; i< LED_COUNT; ++i){
        led_item = cJSON_GetArrayItem(leds, i);
        if (led_item == NULL){
            fprintf(stderr, "[error] cJSON_GetArrayItem %d error.\n", i);
            continue;
        }
        if (!strcasecmp(led_item->valuestring, "on")){
            fprintf(stdout, "[log] ready to light led[%d].\n", i);
            light_led(i);
            led_result[i] = 1;
        }else if (!strcasecmp(led_item->valuestring, "off")){
            fprintf(stdout, "[log] ready to close led[%d].\n", i);
            close_led(i);
            led_result[i] = 0;
        }else{
            fprintf(stderr, "[error] cJSON invilad string %s error.\n", led_item->valuestring);
            continue;
        }
    }
}


/*
 * {"cmd" : "setFan", "fan" : "on"}
 */

void control_fan(cJSON *root)
{
    cJSON*      fan;

    if (root == NULL){
        fprintf(stderr, "[error] control fan error");
        return;
    }

    fan = cJSON_GetObjectItem(root, "fan");
    if (fan == NULL){
        fprintf(stderr, "[error] cJSON_GetObjectItem 'fan' error\n");
        return;
    }

    fan_init();

    if (!strcasecmp(fan->valuestring, "On")){
        open_fan(0);
    }else if (!strcasecmp(fan->valuestring, "off")){
        close_fan();
    }else{
        fprintf(stderr, "[error] unkown operation for fan.\n");
    }
}
